﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Navigant.Infrastructure.Cases;
using Navigant.Infrastructure.DomainBase;

namespace Navigant.Model.Cases
{
    public abstract class CaseChildItem : EntityBase, ICaseChildItem
    {
        #region Private Fields

        private int id;
        private ICase parentCase;
        private List<string> updatedProperties;
        private Dictionary<string, string> extendedProperties;
        private bool extendedPropertiesLoaded;

        #endregion

        #region Constructors

        protected CaseChildItem(int id, ICase parentCase) 
            : base(id)
        {
            this.id = id;
            this.parentCase = parentCase;
            this.extendedProperties = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            this.updatedProperties = new List<string>();
            this.KeyChanged += new EventHandler(CaseChildItem_KeyChanged);
        }

        #endregion

        #region CaseChildItem_KeyChanged

        private void CaseChildItem_KeyChanged(object sender, EventArgs e)
        {
            if (this.Key != null && this.Key is int)
            {
                this.id = (int)this.Key;
            }
        }

        #endregion

        #region AddUpdatedProperty

        protected void AddUpdatedProperty(string propertyName)
        {
            if (!this.updatedProperties.Contains(propertyName))
            {
                this.updatedProperties.Add(propertyName);
            }
        }

        #endregion

        #region LoadExtendedProperties

        protected abstract void LoadExtendedProperties();

        #endregion

        #region ICaseChildItem Members

        public int Id
        {
            get { return this.id; }
        }

        public ICase ParentCase
        {
            get { return this.parentCase; }
        }

        public ReadOnlyCollection<string> UpdatedProperties
        {
            get
            {
                return this.updatedProperties.AsReadOnly();
            }
        }

        public ReadOnlyDictionary<string, string> ExtendedProperties
        {
            get
            {
                // Lazy load the extended properties
                if (!this.extendedPropertiesLoaded || 
                    this.extendedProperties.Count == 0)
                {
                    this.LoadExtendedProperties();
                    this.extendedPropertiesLoaded = true;
                }
                return new ReadOnlyDictionary<string, string>(this.extendedProperties);
            }
        }

        public void SetExtendedProperty(string propertyName, string propertyValue)
        {
            this.SetExtendedProperty(propertyName, propertyValue, false);
        }

        public void SetExtendedProperty(string propertyName, string propertyValue, bool loading)
        {
            this.extendedProperties[propertyName] = propertyValue;
            if (!loading)
            {
                this.AddUpdatedProperty(propertyName);
            }
            // Make sure the property values do not get overwritten
            this.extendedPropertiesLoaded = true;
        }

        #endregion

        #region Equality Tests

        /// <summary>
        /// Determines whether the specified entity is equal to the 
        /// current instance.
        /// </summary>
        /// <param name="entity">An <see cref="System.Object"/> that 
        /// will be compared to the current instance.</param>
        /// <returns>True if the passed in entity is equal to the 
        /// current instance.</returns>
        public override bool Equals(object entity)
        {
            return entity != null
                && entity is CaseChildItem
                && this == (CaseChildItem)entity;
        }

        /// <summary>
        /// Operator overload for determining equality.
        /// </summary>
        /// <param name="base1">The first instance of an 
        /// <see cref="EntityBase"/>.</param>
        /// <param name="base2">The second instance of an 
        /// <see cref="EntityBase"/>.</param>
        /// <returns>True if equal.</returns>
        public static bool operator ==(CaseChildItem base1,
            CaseChildItem base2)
        {
            // check for both null (cast to object or recursive loop)
            if ((object)base1 == null && (object)base2 == null)
            {
                return true;
            }

            // check for either of them == to null
            if ((object)base1 == null || (object)base2 == null)
            {
                return false;
            }

            if (base1.Id != base2.Id || base1.ParentCase != base2.ParentCase)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Operator overload for determining inequality.
        /// </summary>
        /// <param name="base1">The first instance of an 
        /// <see cref="EntityBase"/>.</param>
        /// <param name="base2">The second instance of an 
        /// <see cref="EntityBase"/>.</param>
        /// <returns>True if not equal.</returns>
        public static bool operator !=(CaseChildItem base1,
            CaseChildItem base2)
        {
            return (!(base1 == base2));
        }

        /// <summary>
        /// Serves as a hash function for this type.
        /// </summary>
        /// <returns>A hash code for the Custodian.</returns>
        public override int GetHashCode()
        {
            return this.parentCase.GetHashCode() ^
                this.id.GetHashCode();
        }

        #endregion
    }
}
